package parser;

import model.Arc;
import model.Graph;
import model.Noeud;
import test.Config;
import view.drawable.NoeudDrawable;

import java.awt.*;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * User: Thomas
 * Date: 24/02/14
 * Time: 14:50
 * Classe permettant la génération d'un graph aléatoire avec un nombre de noeud défini
 */
public class GraphGenerator {
    //Nombre de noeud choisi
    private int nbNoeud = 0;

    public GraphGenerator(int nbNoeud) {
        this.nbNoeud = nbNoeud;
    }

    /**
     * Méthode permettant la géneration d'un graph aléatoire
     * @param filenameGraph fichier de sauvegarde du graph
     * @return le Graph généré
     * @throws FileNotFoundException
     * @throws UnsupportedEncodingException
     */
    public Graph generate(String filenameGraph) throws FileNotFoundException, UnsupportedEncodingException {

        //Liste des arcs générés
        HashMap<String, ArrayList<Arc>> arcs = new HashMap<String, ArrayList<Arc>>();
        //Liste des noeuds générés
        HashMap<Integer, Noeud> noeuds = new HashMap<Integer, Noeud>();
        //Liste des noeuds reliés dans le graph
        ArrayList<Noeud> noeuds_used = new ArrayList<Noeud>();
        //Nombre de ligne alétoire (pondéré par le nombre de noeud / 2 afin que les lignes contiennent au moins 2 noeuds)
        int nbLigne = new Random().nextInt((this.getNbNoeud() / 2) - 1) + 1;
        //Ligne connecté au graph afin de formé un graph connexe
        boolean[] lignesConnected = new boolean[nbLigne+1];
        int nbLigneConnected = 0;

        System.out.println("nbLigne "+nbLigne);

        //Génération aléatoire de nbNoeud
        for (int i = 0; i < this.getNbNoeud(); i++) {
            Noeud noeud = new Noeud(i,String.valueOf(i));
            NoeudDrawable noeudDrawable = new NoeudDrawable(new Point(0,0),new Dimension(10,10),noeud);
            noeud.setNoeudDrawable(noeudDrawable);
            noeuds.put(i, noeud);
        }

        //Génération aléatoire des arcs afin de formé des lignes

        //Variable de positionnement graphique des noeuds
        int x = 20;
        int y = 20;
        boolean toRight = true;

        //Itération pour avoir nbLigne en commencant à la ligne 1
        for (int i = 1; i < nbLigne+1; i++) {
            //Création d'une nouvelle ligne
            arcs.put(String.valueOf(i),new ArrayList<Arc>());
            System.out.print("ligne " + i + ": ");

            //nombre de noeud par ligne sachant qu'il faut au moins 2 noeuds par ligne restante parmis les noeuds non utilisés
            int nbNoeudByLigne = new Random().nextInt((this.getNbNoeud() - noeuds_used.size() - (2*(nbLigne - i))) - 2) + 2;
            //si c'est la derniere ligne à générer on prend tous les noeuds restants
            if (i == nbLigne) {
                nbNoeudByLigne = this.getNbNoeud() - noeuds_used.size();
            }
            //Noeud permettant de lier les noeuds avec le noeud tiré aléatoire juste avant
            Noeud lastNoeudRandom = null;
            for (int j = 0; j < nbNoeudByLigne; j++) {
                //Tirage aléatoire d'un noeud qui n'est pas encore relié
                Noeud noeudRandom;
                do {
                    int idNoeud = new Random().nextInt(this.getNbNoeud());
                    noeudRandom = noeuds.get(idNoeud);
                } while (noeuds_used.contains(noeudRandom)); //tant que le noeud est déjà relié

                noeuds_used.add(noeudRandom);
                System.out.print(noeudRandom.getId() + " ");
                //si un noeud a déjà été tiré
                if (lastNoeudRandom != null) {
                    //Création d'un arc avec un cout aléatoire
                    int cout = new Random().nextInt(200 - 20) + 20;
                    //Variable aléatoire pour définir si la liaison est dans les 2 sens
                    boolean isAller = (new Random().nextInt(100) < 95);
                    boolean isRetour = !isAller || (new Random().nextInt(100) < 95);
                    if (isAller) {
                        arcs.get(String.valueOf(i)).add(new Arc(lastNoeudRandom, noeudRandom, String.valueOf(i), cout));
                    }
                    if (isRetour) {
                        arcs.get(String.valueOf(i)).add(new Arc(noeudRandom, lastNoeudRandom, String.valueOf(i), cout));
                    }
                    System.out.println(noeudRandom.getId() + "->" + lastNoeudRandom.getId());
                }

                //gestion du positionnement graphique
                noeudRandom.getNoeudDrawable().setPosition(new Point(x,y));
                lastNoeudRandom = noeudRandom;

                if (toRight) {
                    x += Config.NOEUD_DISTANCE;
                } else {
                    x -= Config.NOEUD_DISTANCE;
                }

                //demi tour si x > 2000 ou < 20
                if (x > 2000 || x < 20) {
                    toRight = !toRight;
                    y += Config.LIGNE_DISTANCE / 2;
                }
            }
            System.out.println();
            y += Config.LIGNE_DISTANCE;
        }

        //Génération minimal des correspondances afin de faire un graph connexe
        arcs.put("0",new ArrayList<Arc>());
        Set<String> lignes = arcs.keySet();
        if (nbLigne > 1) {
            //Tant que toutes les lignes ne sont pas connectées
            while (nbLigneConnected != nbLigne) {
                //tirage aléatoire de 2 lignes
                int ligne = new Random().nextInt(lignes.size() - 1) + 1;
                int ligne2 = new Random().nextInt(lignes.size() - 1) + 1;

                //tirage aléatoire de la ligne 2 tant que la ligne choisie est déjà connectée
                while (lignesConnected[ligne2] || ligne == ligne2) {
                    ligne2 = new Random().nextInt(lignes.size() - 1) + 1;
                }
                lignesConnected[ligne2] = true;
                nbLigneConnected++;
                System.out.println(ligne + " " + ligne2);

                //ajout de la correspondance entre les 2 lignes
                addCorrespondanceRandBetween2Lignes(ligne, ligne2, arcs);
            }
        }

        //Ajout de correspondance aléatoire non nécessaire
        int nbCorrespondance = new Random().nextInt(this.getNbNoeud());
        for (int i = 0; i < nbCorrespondance; i++) {
            //tirage aléatoire de 2 lignes
            int ligne = new Random().nextInt(lignes.size() - 1) + 1;
            int ligne2 = new Random().nextInt(lignes.size() - 1) + 1;

            //tirage aléatoire de la ligne 2 tant que la ligne choisie est déjà connectée
            while (ligne == ligne2) {
                ligne2 = new Random().nextInt(lignes.size() - 1) + 1;
            }

            //ajout de la correspondance entre les 2 lignes
            addCorrespondanceRandBetween2Lignes(ligne,ligne2,arcs);
        }

        //Sauvegarde du graph et des positions
        Graph graph = new Graph(arcs,noeuds);
        ParserGraph.save(filenameGraph,graph);
        GraphFilePxPosition.save(graph);

        return graph;
    }

    /**
     * Méthode permettant l'ajout d'une correspondance aléatoire entre 2 lignes choisies
     * @param ligne première ligne choisie
     * @param ligne2 deuxième ligne choisie
     * @param arcs liste des arcs du graph
     */
    private void addCorrespondanceRandBetween2Lignes(int ligne,int ligne2,HashMap<String, ArrayList<Arc>> arcs) { //todo à améliorer

        //tirage aléatoire de 2 arcs dans les 2 lignes afin de relié les sources de ces 2 arcs
        int arcLigneRand = new Random().nextInt(arcs.get(String.valueOf(ligne)).size());
        int arcLigne2Rand = new Random().nextInt(arcs.get(String.valueOf(ligne2)).size());
        Arc arcLigne = arcs.get(String.valueOf(ligne)).get(arcLigneRand);
        Arc arcLigne2 = arcs.get(String.valueOf(ligne2)).get(arcLigne2Rand);

        //ajout d'un noeud de lien dans la ligne 1
        arcLigne.getSource().setName(arcLigne2.getSource().getName());

        int cout = new Random().nextInt(200 - 20) + 20;
        arcs.get("0").add(new Arc(arcLigne.getSource(), arcLigne2.getSource(), String.valueOf("0"), cout));
        arcs.get("0").add(new Arc(arcLigne2.getSource(), arcLigne.getSource(), String.valueOf("0"), cout));
    }

    public int getNbNoeud() {
        return nbNoeud;
    }

    public void setNbNoeud(int nbNoeud) {
        this.nbNoeud = nbNoeud;
    }

    public static void main(String[] args) {
        try {
            new GraphGenerator(20).generate("test.graph");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}
